﻿using AMS.Common.ToolChains.WeComEncrypt;
using DynamicAuthorization.Sdk.Attributes;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using OA.Infrastructure.DomainModels.Callback;
using OA.Infrastructure.WeCom;
using OA.Infrastructure.WeCom.Abstractions;

namespace OA.Infrastructure.Controllers;

[Route("api/[controller]")]
[ApiController]
[PermisstionDefinition("WeCom集成管理", Route = "/organization/user")]
public class WeComController : ControllerBase
{
    private readonly IWeComService _service;
    private readonly DbContext _context;
    private readonly IAccountService _accountService;
    private readonly IMemoryCache _cache;
    private readonly WeComOptions _options;
    private readonly ILogger<WeComController> _logger;

    private readonly string _redirectUri;

    public WeComController(
        IWeComService service,
        DbContext context,
        IAccountService accountService,
        IMemoryCache cache,
        IOptions<WeComOptions> options,
        ILogger<WeComController> logger)
    {
        _service = service;
        _context = context;
        _accountService = accountService;
        _cache = cache;
        _options = options.Value;
        _logger = logger;

        _redirectUri = $"{_service.AppEntryUrl}/oauth";
    }

    [HttpGet]
    public async Task<IActionResult> ReceiveCode([FromQuery] string code)
    {
        _logger.LogInformation("Code: {code}", code);

        if (!_cache.Get<bool>(code))
        {
            _cache.Set("code", code, TimeSpan.FromMinutes(5));

            var token = await _service.GetAccessTokenAsync();
            var userInfo = await _service.GetUserInfoAsync(code);

            _logger.LogInformation("access_token: {token}", token.Data);
            _logger.LogInformation("userid: {userid}\nuser_ticket: {user_ticket}", userInfo.Data.UserId, userInfo.Data.User_ticket);

            if (!string.IsNullOrEmpty(userInfo.Data.User_ticket))
            {
                var privateUser = await _service.SyncUserPrivateInfoAsync(userInfo.Data.User_ticket, HttpContext.RequestAborted);

                if (privateUser.Status == OperationalResult.SUCCESS)
                {
                    var user = await _context.Set<User>().FirstOrDefaultAsync(x => x.UserId == privateUser.Data.UserId, HttpContext.RequestAborted);

                    if (user is not null)
                    {
                        user.Gender = privateUser.Data.Gender == "1"
                        ? Enums.Gender.Male
                        : privateUser.Data.Gender == "2"
                            ? Enums.Gender.Female
                            : Enums.Gender.Undefined;

                        user.Avatar = privateUser.Data.Avatar;
                        user.QrCode = privateUser.Data.Qr_code;
                        user.Mobile = privateUser.Data.Mobile;
                        user.Email = privateUser.Data.Email;
                        user.BizMail = privateUser.Data.Biz_mail;
                        user.Address = privateUser.Data.Address;

                        user.UserTicket = userInfo.Data.User_ticket;

                        _context.Set<User>().Update(user);

                        await _context.SaveChangesAsync(HttpContext.RequestAborted);
                    }
                }
            }

            var userEntity = await _context.Set<User>().FirstOrDefaultAsync(n => n.UserId == userInfo.Data.UserId, HttpContext.RequestAborted);

            if (userEntity is null)
            {
                return Unauthorized("用户不存在");
            }

            var loginInfo = new LoginInfo
            {
                Account = userEntity.Account,
                Password = userEntity.Password
            };

            var authResult = await _accountService.LoginAsync(loginInfo, HttpContext.RequestAborted);

            _cache.Set(userInfo.Data.UserId, authResult.Data);

            return Redirect($"{_redirectUri}?uid={userInfo.Data.UserId}");
        }

        return Redirect(_redirectUri);
    }

    [HttpGet("qrcode")]
    public async Task<IActionResult> ReceiveCodeFromQRCode([FromQuery] string code)
    {
        _logger.LogInformation("Code: {code}", code);

        if (!_cache.Get<bool>(code))
        {
            _cache.Set(code, true, TimeSpan.FromMinutes(5));

            var token = await _service.GetAccessTokenAsync();
            var userInfo = await _service.GetUserInfoAsync(code);

            _logger.LogInformation("access_token: {token}", token.Data);
            _logger.LogInformation("userid: {userid}\nuser_ticket: {user_ticket}", userInfo.Data.UserId, userInfo.Data.User_ticket);

            var userEntity = await _context.Set<User>().FirstOrDefaultAsync(n => n.UserId == userInfo.Data.UserId, HttpContext.RequestAborted);

            if (userEntity is null)
            {
                return Unauthorized("用户不存在");
            }

            var loginInfo = new LoginInfo
            {
                Account = userEntity.Account,
                Password = userEntity.Password
            };

            var authResult = await _accountService.LoginAsync(loginInfo, HttpContext.RequestAborted);

            _cache.Set(userInfo.Data.UserId, authResult.Data);

            return Redirect($"{_redirectUri}?uid={userInfo.Data.UserId}");
        }

        return Redirect(_redirectUri);
    }

    [HttpGet("auth")]
    public async Task<IActionResult> LoginAsync([FromQuery] string uid)
    {
        Console.WriteLine($"有人认证：【{uid}】");

        if (_cache.TryGetValue<LoginResult>(uid, out var result))
        {
            return Ok(result);
        }

        var user = await _context.Set<User>().FirstOrDefaultAsync(n => n.UserId == uid, HttpContext.RequestAborted);

        if (user is null)
        {
            return Unauthorized("用户不存在");
        }

        var loginInfo = new LoginInfo
        {
            Account = user.Account,
            Password = user.Password
        };

        var authResult = await _accountService.LoginAsync(loginInfo, HttpContext.RequestAborted);

        return authResult.Status == OperationalResult.SUCCESS ? Ok(authResult.Data) : Unauthorized();
    }

    [HttpGet("sync")]
    [DynamicAuthorize]
    [PermisstionDefinition("通讯录同步")]
    public async Task<IActionResult> OrganizationSyncAsync()
    {
        var result = await _service.GetDepartmentsAsync();

        if (result.Status == OperationalResult.SUCCESS)
        {
            var normalRole = await _context.Set<Role>().FirstAsync(n => n.Name == "普通用户", HttpContext.RequestAborted);
            var currentDepts = await _context.Set<Department>().Select(n => n.Name).ToListAsync();
            var currentUsers = await _context.Set<User>().Select(n => n.UserId).ToListAsync();

            var wecomDepts = result.Data;

            var userInsertList = new List<User>();

            foreach (var dept in wecomDepts)
            {
                if (currentDepts.All(n => n != dept.Name))
                {
                    var dbDept = await _context.Set<Department>().FirstOrDefaultAsync(x => x.Id == dept.Id, HttpContext.RequestAborted);

                    if (dbDept is null)
                    {
                        dept.Roles.Add(normalRole);

                        await _context.Set<Department>().AddAsync(dept, HttpContext.RequestAborted);
                    }
                    else
                    {
                        dbDept.Name = dept.Name;

                        _context.Set<Department>().Update(dbDept);
                    }
                }

                var usersResult = await _service.GetDepartmentUsersAsync(dept);

                if (usersResult.Status == OperationalResult.SUCCESS)
                {
                    foreach (var user in usersResult.Data)
                    {
                        if (currentUsers.All(n => n != user.UserId))
                        {
                            user.Roles.Add(normalRole);
                            userInsertList.Add(user);
                        }
                    }
                }
                else
                {
                    result.Status = OperationalResult.FAILURE;
                    result.Message = usersResult.Message;

                    return Ok(result);
                }
            }

            await _context.SaveChangesAsync(HttpContext.RequestAborted);

            await _context.Set<User>().AddRangeAsync(userInsertList, HttpContext.RequestAborted);

            var leadersDept = new List<Department>();

            foreach (var user in userInsertList.Where(x => x.IsLeader))
            {
                var leadDept = await _context.Set<Department>().FirstAsync(x => x.Id == user.Department!.Id, HttpContext.RequestAborted);
                leadDept.Leader = user;
                leadersDept.Add(leadDept);
            }

            foreach (var dept in wecomDepts.Where(x => x.Parent != null))
            {
                dept.Parent = wecomDepts.First(x => x.Id == dept.Parent?.Id);

                _context.Set<Department>().Update(dept);
            }

            _context.Set<Department>().UpdateRange(leadersDept);

            await _context.SaveChangesAsync(HttpContext.RequestAborted);

            return Ok(new OperationalResult { Message = "同步成功" });
        }

        return Ok(result);
    }

    [HttpGet("message")]
    [HttpPost("message")]
    public async Task<IActionResult> ReceiveMessageAsync([FromQuery] string msg_signature, long timestamp, long nonce, string? echostr)
    {
        if (string.IsNullOrEmpty(_options.Token) || string.IsNullOrEmpty(_options.AesKey))
        {
            return BadRequest();
        }

        var wxcpt = new WXBizMsgCrypt(_options.Token, _options.AesKey, _options.CorpId);

        #region 该部分为企业微信验证消息服务器代码，请勿删除        
        if (!string.IsNullOrEmpty(echostr))
        {
            var echostr_origin = string.Empty;

            return wxcpt.VerifyURL(msg_signature, timestamp.ToString(), nonce.ToString(), echostr, ref echostr_origin) == 0
                ? Ok(echostr_origin)
                : BadRequest();
        }
        #endregion

        using var reader = new StreamReader(Request.Body);

        var msgCiphertext = await reader.ReadToEndAsync();

        var msg = string.Empty;
        var ret = wxcpt.DecryptMsg(msg_signature, timestamp.ToString(), nonce.ToString(), msgCiphertext, ref msg);

        if (ret != 0)
        {
            _logger.LogError("Error: 消息解密失败, ret: {ret}", ret);
        }

        _logger.LogInformation("回调事件消息：{str}", msg);

        var dispatcher = HttpContext.RequestServices.GetRequiredService<IWeComMessageDispatcher>();

        try
        {
            if (dispatcher.Dispatch(msg).TryGetSerializer(out var serializer))
            {
                using var textReader = new StringReader(msg);
                var model = serializer.Deserialize(textReader) as WeComCallbackEventBase;

                if (model is not null)
                {
                    var result = await model.CallbackAsync();
                }

                return Ok(model);
            }
        }
        catch (Exception ex)
        {
            _logger.LogError("Error: {e}", ex.Message);
        }

        return BadRequest();
    }

#if DEBUG
    [HttpGet("token")]
    public async Task<IActionResult> GetAccessTokenAsync()
    {
        return Ok(await _service.GetAccessTokenAsync());
    }
#endif
}